#include "map.h"
#include "resources.h"

int map_index (Map* m, int x, int y, int z) 
{
	assert ((x < m->width) && (y < m->height) && (z < m->num_layers));
	return z*m->width*m->height + y*m->width + x;
}

// Get pointer from coords
Tile* map_tile (Map* m, int x, int y, int z)
{
	return &m->tile[map_index (m,x,y,z)];
}

// Initializes a map with a single layer.
void map_init(Map* m, int w, int h)
{
	memset (m, 0, sizeof (Map));
	m->num_layers = 1;
	m->width = w;
	m->height = h;
	m->tile = malloc (m->num_layers * m->width * m->height * sizeof(Tile));
	
	for(int y=0; y<m->height; ++y)
	for(int x=0; x<m->width ; ++x) {
		map_tile(m, x, y, 0)->type = TILE_UNDEFINED;
	}
}

static void draw_tile (BITMAP* target, TileType which, int frame, int sx, int sy)
{
	int tx;
	int ty;
	
	switch (which) {
		
	case TILE_FLOOR: // Draw floor
		tx = 0; ty = 0;
		break;
	
	case TILE_WALL:
	case TILE_TOGGLEABLE:
		// tx = 1; ty = 2; break; // debug temp: disregard frames
		tx = frame % 6; ty = frame / 6;
		break;
	
	case TILE_DOOR:
		tx = 4 + frame % 2; ty = frame / 2;
		break;
	
	case TILE_TELEPORTER:
		tx = 1 + frame; ty = 0;
		break;
	
	case TILE_TRIGGER:
		tx = 3; ty = 1;
		break;
	
	case TILE_EXIT_UP:
		tx = 7; ty = 4;
		break;
	
	case TILE_EXIT_DOWN:
		tx = 7; ty = 3;
		break;
	
	default: // Draw nothing
		rectfill(target, sx, sy, sx+TILE_SIZE-1, sy+TILE_SIZE-1, 0);
		return;
	
	}
	
	blit(bmp.tileset, target, 
			tx * TILE_SIZE, ty * TILE_SIZE, 
			sx, sy, 
			TILE_SIZE, TILE_SIZE);
}

void map_display(Map* m, BITMAP* target, coord_t player, int* xoffset, int* yoffset)
{	
	*xoffset = WIDTH/2 - player.x;
	*yoffset = HEIGHT/2 - player.y;

	int z = player.z;
	assert (z < m->num_layers);
	
	// bounds in tile units of on-screen visible map
	int min_x = MAX (0, (-(*xoffset) / TILE_SIZE));
	int min_y = MAX (0, (-(*yoffset) / TILE_SIZE));
	int max_x = MIN ( m->width , ((-(*xoffset)+SCREEN_W) / TILE_SIZE) +1 );
	int max_y = MIN ( m->height, ((-(*yoffset)+SCREEN_H) / TILE_SIZE) +1 );

	for (int y = min_y; y < max_y; y++)
	for (int x = min_x; x < max_x; x++) {
		Tile* t = map_tile(m,x,y,z);
		draw_tile (target, t->type, t->frame, (*xoffset) + x * TILE_SIZE, (*yoffset) + y * TILE_SIZE);
	}
	
	/* #if TOWERHACK_DEBUG // Debug output for screen-area-restricted map draw
	textprintf_ex (buffer, font, 5, 5, makecol (255, 0, 0), 0, "min_x = %d", min_x);
	textprintf_ex (buffer, font, 5, 15, makecol (255, 0, 0), 0, "min_y = %d", min_y);
	textprintf_ex (buffer, font, 5, 25, makecol (255, 0, 0), 0, "max_x = %d", max_x);
	textprintf_ex (buffer, font, 5, 35, makecol (255, 0, 0), 0, "max_y = %d", max_y);
	#endif */
}

void map_decorate (Map* m) 
{		
	for (int z = 0; z < m->num_layers; z++)
	for (int y = 0; y < m->height; y++)
	for (int x = 0; x < m->width; x++) {
		Tile* t = map_tile(m, x, y, z);
		
		// Convert everything undefined to walls
		if (t->type == TILE_UNDEFINED) t->type = TILE_WALL;
		
		// Decorate walls
		#define IFWALL(x,y,z) ((((x) >= 0) && ((y) >= 0) && ((x) < m->width) && ((y) < m->height)) ? \
			(map_tile(m,(x),(y),(z))->type != TILE_WALL) && (map_tile(m,(x),(y),(z))->type != TILE_UNDEFINED) : 0)
		#define CHECKMASK(m) ((mask & (m)) == (m))
		
		if (t->type == TILE_WALL) {
			int mask =
				(IFWALL(x-1,y-1,z) * 128) +
				(IFWALL(x,y-1,z) * 64) +
				(IFWALL(x+1,y-1,z) * 32) +
				(IFWALL(x+1,y,z) * 16) +
				(IFWALL(x+1,y+1,z) * 8) +
				(IFWALL(x,y+1,z) * 4) +
				(IFWALL(x-1,y+1,z) * 2) +
				(IFWALL(x-1,y,z) * 1);
			
			if (CHECKMASK(64+32+16)) t->frame = FRAME_WALL_UPRIGHT;
			else if (CHECKMASK(1+128+64)) t->frame = FRAME_WALL_UPLEFT;
			else if (CHECKMASK(16+8+4)) t->frame = FRAME_WALL_DOWNRIGHT;
			else if (CHECKMASK(4+2+1)) t->frame = FRAME_WALL_DOWNLEFT;
			else if (CHECKMASK(64+4)) t->frame = FRAME_WALL_UPDOWN;
			else if (CHECKMASK(1+16)) t->frame = FRAME_WALL_LEFTRIGHT;
			else if (CHECKMASK(32+2)) t->frame = FRAME_WALL_CORN_DIAG1;
			else if (CHECKMASK(128+8)) t->frame = FRAME_WALL_CORN_DIAG2;
			else if (CHECKMASK(64)) t->frame = FRAME_WALL_UP;
			else if (CHECKMASK(16)) t->frame = FRAME_WALL_RIGHT;
			else if (CHECKMASK(4)) t->frame = FRAME_WALL_DOWN;
			else if (CHECKMASK(1)) t->frame = FRAME_WALL_LEFT;
			else if (CHECKMASK(128+32)) t->frame = FRAME_WALL_CORN_UP;
			else if (CHECKMASK(2+128)) t->frame = FRAME_WALL_CORN_LEFT;
			else if (CHECKMASK(32+8)) t->frame = FRAME_WALL_CORN_RIGHT;
			else if (CHECKMASK(8+2)) t->frame = FRAME_WALL_CORN_DOWN;
			else if (CHECKMASK(128)) t->frame = FRAME_WALL_CORN_UPLEFT;
			else if (CHECKMASK(32)) t->frame = FRAME_WALL_CORN_UPRIGHT;
			else if (CHECKMASK(8)) t->frame = FRAME_WALL_CORN_DOWNRIGHT;
			else if (CHECKMASK(2)) t->frame = FRAME_WALL_CORN_DOWNLEFT;
			else t->frame = FRAME_WALL_MID; 
		}
	}
}

void map_extend (Map* m, int z)
{
	#if TOWERHACK_DEBUG
	fprintf (stderr, "[Debug] map_extend from %d layers to %d layers.\n", m->num_layers, z + 1);
	#endif
	assert(z >= m->num_layers);
	m->num_layers = z + 1;
	m->tile = realloc (m->tile, m->num_layers * m->width * m->height * sizeof(Tile));
	
	for (int y=0; y<m->height; ++y)
	for (int x=0; x<m->width ; ++x) {
		map_tile(m, x, y, m->num_layers-1)->type = TILE_UNDEFINED;
	}
}
